package com.jmxcfc.blfsc.ssq.service.contract.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jmxcfc.blfsc.common.common.enums.ProcessStateEnum;
import com.jmxcfc.blfsc.common.common.request.BaseRequest;
import com.jmxcfc.blfsc.common.common.response.BaseResponse;
import com.jmxcfc.blfsc.common.exception.AfterOutboundException;
import com.jmxcfc.blfsc.common.exception.BeforeOutboundException;
import com.jmxcfc.blfsc.common.exception.OutboundException;
import com.jmxcfc.blfsc.common.exception.ValidBodyException;
import com.jmxcfc.blfsc.common.sign.SignRequest;
import com.jmxcfc.blfsc.common.sign.extend.SignExtend;
import com.jmxcfc.blfsc.common.sms.BaseContext;
import com.jmxcfc.blfsc.common.validation.rule.RequestFieldRule;
import com.jmxcfc.blfsc.common.validation.rule.SignRulesUtil;
import com.jmxcfc.blfsc.ssq.common.Constants;
import com.jmxcfc.blfsc.ssq.entity.UploadContractSub;
import com.jmxcfc.blfsc.ssq.modle.response.SsqResponse;
import com.jmxcfc.blfsc.ssq.service.SignatureService;
import com.jmxcfc.blfsc.ssq.service.contract.IUploadContractRequestService;
import com.jmxcfc.blfsc.ssq.service.contract.IUploadContractSubService;
import com.jmxcfc.blfsc.ssq.util.FileUtils;
import com.jmxcfc.blfsc.ssq.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.io.FileInputStream;
import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 功能描述
 *
 * @author: pbs
 * @date: 2024年09月04日 0:06
 */
@Service
@Slf4j
public class UploadContractRequestServiceImpl implements IUploadContractRequestService {

    @Value("${com.ssq.uploadContract.url}")
    private String uploadContractUrl;


    /**
     * 合同有效时间,单位秒s
     */
    @Value("${com.ssq.uploadContract.expireTime}")
    private long expireTime;


    private SignatureService signatureService;

    private IUploadContractSubService uploadContractSubService;

    private RestTemplate restTemplate;

    @Autowired
    public void setSignatureService(SignatureService signatureService) {
        this.signatureService = signatureService;
    }
    @Autowired
    public void setUploadContractSubService(IUploadContractSubService uploadContractSubService) {
        this.uploadContractSubService = uploadContractSubService;
    }
    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public BaseResponse<UploadContractSub> sendRequest(BaseRequest<SignRequest<SignExtend>> requestMessage) {
        BaseContext<String> baseContext = new BaseContext<>();
        //头部参数校验规则:业务号
        List<RequestFieldRule<?>> headerElementRules = baseContext.getHeaderElementRules();
        headerElementRules.add(SignRulesUtil.getRequestNoRule());

        //请求体参数校验: 账号(证件号)、合同pdf的路径
        List<RequestFieldRule<?>> bodyElementRules = baseContext.getBodyElementRules();
        bodyElementRules.add(SignRulesUtil.getIdCodeRule());
        bodyElementRules.add(SignRulesUtil.getTemplePdfPathRule());

        List<RequestFieldRule<?>> bodyExtendElementRules = baseContext.getBodyExtendElementRules();
        bodyExtendElementRules.add(SignRulesUtil.getPageNumRule());
        return execute(requestMessage, baseContext);
    }



    /**
     * 重写扩展字段的校验:合同模板页数
     * @param body (不能传JSONObject)
     *备注:每个实现类需要自己实现该方法,然后进行扩展字段的校验
     * @param baseHandleContext
     * @param <E>
     * @throws ValidBodyException
     */
    @Override
    public <E> void validBodyExtend(SignRequest<SignExtend> body, BaseContext<E> baseHandleContext) throws ValidBodyException {
        try {
            if (Objects.nonNull(baseHandleContext)) {
                baseHandleContext.getBodyExtendElementRules().forEach(rule -> valid(rule, body.getExtend()));
            }
        } catch (RuntimeException e) {
            throw new ValidBodyException(e.getMessage());
        }
    }


    /**
     * 1、校验参数:account账号(idCode)、pageNum、templePdfPath(待签章pdf文件路径)
     * @param request
     * @param response
     * @param baseContext
     * @throws BeforeOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void beforeOutbound(BaseRequest<SignRequest<SignExtend>> request, BaseResponse<UploadContractSub> response, BaseContext<String> baseContext) throws BeforeOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try {
            log.info("请求业务号requestNo:{},上传并创建合同接口,前置处理:数据落库和参数组装", requestNo);
            UploadContractSub uploadContractSub = new UploadContractSub();
            uploadContractSub.setRequestNo(requestNo);

            //参数组织、regUserPersonalSub设置值
            String requestParamStr = buildParam(request, uploadContractSub);
            //由于是签名参数追加为url参数:/openapi/v2/user/reg/?developerId=aaaaaa&rtick=14840209310001&signType=rsa
            String urlPathParams = signatureService.buildUrlPathParams(uploadContractUrl, "", requestParamStr);
            //请求路径的url的参数
            baseContext.setExtendStr(urlPathParams);
            //设置请求
            baseContext.setRestHttpEntity(HttpUtils.creatRequestEntity(requestParamStr));
            //保存数据
            uploadContractSubService.save(uploadContractSub);
            //主键
            baseContext.setRecordId(uploadContractSub.getId());
            log.info("请求业务号requestNo:{},上传并创建合同接口,前置处理结束", requestNo);
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},前置处理异常:{}", requestNo, e.getMessage(), e);
            throw new BeforeOutboundException(e.getMessage(), e);
        }
    }

    @Override
    public void outbound(BaseRequest<SignRequest<SignExtend>> request, BaseResponse<UploadContractSub> response, BaseContext<String> baseContext) throws OutboundException {
        try{
            //请求url的,必须要用URL,否则restTemplate会对url进行
            URI requestUrl = new URI( StringUtils.join(uploadContractUrl, baseContext.getExtendStr()));
            log.info("请求业务号requestNo:{},上传并创建合同接口,发送请求外呼,请求地址:{},请参数:{}",request.getHeader().getRequestNo(),requestUrl,baseContext.getRestHttpEntity().getBody());
            String resp = restTemplate.postForObject(requestUrl, baseContext.getRestHttpEntity(), String.class);
            log.info("请求业务号requestNo:{},上传并创建合同接口,发送请求外呼,响应内容:{}", request.getHeader().getRequestNo(), resp);
            baseContext.setRestResponse(resp);
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},上传并创建合同接口,发送请求外呼异常:{}", request.getHeader().getRequestNo(), e.getMessage());
            throw new OutboundException(e.getMessage(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void afterOutbound(BaseRequest<SignRequest<SignExtend>> request, BaseResponse<UploadContractSub> response, BaseContext<String> baseContext) throws AfterOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try{
            log.info("请求业务号requestNo:{},上传并创建合同接口,后置处理:解析响应结果", requestNo);
            UploadContractSub uploadContractSub = new UploadContractSub();
            uploadContractSub.setRequestNo(requestNo);
            //注意这边是根据主键更新
            uploadContractSub.setId(baseContext.getRecordId());

            SsqResponse ssqResponse = JSON.parseObject(baseContext.getRestResponse(),SsqResponse.class);
            //错误码值,0:成功
            String errno = ssqResponse.getErrno();
            //错误信息描述
            String errmsg = ssqResponse.getErrmsg();
            log.info("请求业务号requestNo:{},上传并创建合同接口,响应的结果 errno:{},errmsg:{}", requestNo, errno, errmsg);
            if (StringUtils.equals(errno, Constants.ERRNO_0)) {
                JSONObject data = Optional.ofNullable(ssqResponse.getData()).orElseGet(JSONObject::new);
                //合同id
                String contractId = data.getString(Constants.CONTRACT_ID);
                if (StringUtils.isNotBlank(contractId)) {
                    response.getHeader().setProcessState(ProcessStateEnum.SUCCESS);
                    uploadContractSub.setContractId(contractId);
                }else {
                    log.error("请求业务号requestNo:{},上传并创建合同接口响应的contractId字段为空:{}",requestNo,contractId);
                    response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
                }
            }else {
                response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
            }
            uploadContractSub.setErrno(errno);
            uploadContractSub.setErrmsg(errmsg);
            uploadContractSubService.updateById(uploadContractSub);
            response.getHeader().setResCode(errno);
            response.getHeader().setResMessage(errmsg);
            response.setBody(uploadContractSub);
            log.info("请求业务号requestNo:{},上传并创建合同接口接口,后置处理结束", requestNo);
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},上传并创建合同接口接口,后置处理异常:{}", requestNo, e.getMessage(), e);
            throw new AfterOutboundException(e.getMessage(), e);
        }
    }


    /**
     * todo 文件名待确认
     * 1、参数:
     (1)账号:取值证件号码、待签署合同pdf文件地址、
     (2)扩展字段:合同页数pageNum、
     * @param request
     * @param uploadContractSub
     * @return
     */
    private String buildParam(BaseRequest<SignRequest<SignExtend>> request, UploadContractSub uploadContractSub) {
        JSONObject requestParam = new JSONObject();
        SignRequest<SignExtend> signRequestBody = request.getBody();

        //账户(取证件号码)
        requestParam.put(Constants.ACCOUNT, signRequestBody.getIdCode());
        String templePdfPath = signRequestBody.getTemplePdfPath();
        try (FileInputStream inputStream = new FileInputStream(templePdfPath)) {
            byte[] bdata = IOUtils.toByteArray(inputStream);
            //fmd5:文件MD5值
            requestParam.put(Constants.FMD5, DigestUtils.md5Hex(bdata));
            //fdata:文件内容,文件内容Base64字符串
            requestParam.put(Constants.FDATA, Base64.encodeBase64String(bdata));
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},读取合同模板pdf文件:{},进行md5、Base64操作异常:{}", request.getHeader().getRequestNo(), templePdfPath, e.getMessage(), e);
        }
        //合同文件页数
        requestParam.put(Constants.FPAGES, signRequestBody.getExtend().getPageNum());
        //文件名称:文件名必须带上后缀名，例如“XXXX.pdf”,todo 待确认合同文件名称
        String fileName = FileUtils.getFileNameByPath(templePdfPath, false, "");
        requestParam.put(Constants.FNAME, fileName);
        //文件类型:pdf
        requestParam.put(Constants.FTYPE, "pdf");
        //标题
        requestParam.put(Constants.TITLE, fileName);
        //有效期:该合同需要在该时间截止之前完成，未完成合同会过期。建议根据业务实际需要设定。
        // 单位：秒；计算方法：当前系统时间的时间戳秒数+有效期秒数。
        long time = (System.currentTimeMillis() / 1000) + expireTime;
        requestParam.put(Constants.EXPIRE_TIME, String.valueOf(time));

        //带签署合同文件路径
        uploadContractSub.setPdfFilePath(templePdfPath);
        //账号
        uploadContractSub.setAccount(requestParam.getString(Constants.ACCOUNT));
        //文件MD5值
        uploadContractSub.setFmd5(requestParam.getString(Constants.FMD5));
        uploadContractSub.setFpages(requestParam.getString(Constants.FPAGES));
        uploadContractSub.setFname(requestParam.getString(Constants.FNAME));
        uploadContractSub.setFtype(requestParam.getString(Constants.FTYPE));
        uploadContractSub.setTitle(requestParam.getString(Constants.TITLE));
        uploadContractSub.setExpireTime(requestParam.getString(Constants.EXPIRE_TIME));

        log.info("请求业务号requestNo:{},参数组织完成,参数:{}", request.getHeader().getRequestNo(), JSON.toJSONString(requestParam));
        return JSON.toJSONString(requestParam);
    }




}